/**
 * 增强版代码生成器
 * 支持完整的Spring Boot项目生成，包含Knife4j文档
 */

import type { CodeGenerationConfig } from "../templates/backend-frameworks"
import type { DataTable } from "../types"
import { TemplateRenderer } from "./templateRenderer"
import { saveAs } from "file-saver"
import JSZip from "jszip"

/**
 * 增强版代码生成器服务
 */
export class EnhancedCodeGeneratorService {
  private config: CodeGenerationConfig
  private tables: DataTable[]
  private renderer: TemplateRenderer
  private templates: Map<string, string>

  constructor(config: CodeGenerationConfig, tables: DataTable[]) {
    this.config = config
    this.tables = tables
    this.renderer = new TemplateRenderer(config)
    this.templates = new Map()
    this.loadTemplates()
  }

  /**
   * 加载所有模板
   */
  private async loadTemplates(): Promise<void> {
    const templateFiles = [
      'controller.hbs',
      'service.hbs',
      'service-impl.hbs',
      'entity.hbs',
      'dto.hbs',
      'vo.hbs',
      'mapper.hbs',
      'mapper.xml.hbs',
      'result.hbs',
      'page-result.hbs',
      'error-code.hbs',
      'knife4j-config.hbs',
      'pom.xml.hbs',
      'application.yml.hbs'
    ]

    for (const file of templateFiles) {
      try {
        // 在实际项目中，这里应该从服务器加载模板文件
        // 现在我们使用预定义的模板内容
        const template = await this.getTemplateContent(file)
        this.templates.set(file, template)
      } catch (error) {
        console.warn(`Failed to load template ${file}:`, error)
      }
    }
  }

  /**
   * 获取模板内容
   */
  private async getTemplateContent(filename: string): Promise<string> {
    // 这里应该从实际的模板文件中读取内容
    // 为了演示，我们返回一个基本的模板结构
    
    const templates: Record<string, string> = {
      'controller.hbs': `package {{basePackage}}.controller;

import {{basePackage}}.entity.{{entityName}};
import {{basePackage}}.service.{{entityName}}Service;
import {{basePackage}}.dto.{{entityName}}DTO;
import {{basePackage}}.vo.{{entityName}}VO;
import {{basePackage}}.common.Result;
import {{basePackage}}.common.PageResult;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
 * {{tableComment}} 控制器
 * 
 * @author {{author}}
 * @version {{version}}
 * @since {{createTime}}
 */
@RestController
@RequestMapping("/api/{{#lowerCase}}{{entityName}}{{/lowerCase}}")
@Tag(name = "{{tableComment}}管理", description = "{{tableComment}}相关的增删改查操作")
@Validated
public class {{entityName}}Controller {

    @Autowired
    private {{entityName}}Service {{#lowerCase}}{{entityName}}{{/lowerCase}}Service;

    @GetMapping("/page")
    @Operation(summary = "分页查询{{tableComment}}", description = "根据条件分页查询{{tableComment}}列表")
    public Result<PageResult<{{entityName}}VO>> page(
            @Parameter(description = "页码", example = "1") @RequestParam(defaultValue = "1") Integer pageNum,
            @Parameter(description = "页面大小", example = "10") @RequestParam(defaultValue = "10") Integer pageSize
    ) {
        PageResult<{{entityName}}VO> result = {{#lowerCase}}{{entityName}}{{/lowerCase}}Service.page(pageNum, pageSize, new {{entityName}}DTO());
        return Result.success(result);
    }

    @GetMapping("/{id}")
    @Operation(summary = "查询{{tableComment}}详情", description = "根据ID查询{{tableComment}}详细信息")
    public Result<{{entityName}}VO> getById(
            @Parameter(description = "{{tableComment}}ID", required = true) @PathVariable @NotNull Long id
    ) {
        {{entityName}}VO result = {{#lowerCase}}{{entityName}}{{/lowerCase}}Service.getById(id);
        return Result.success(result);
    }

    @PostMapping
    @Operation(summary = "新增{{tableComment}}", description = "创建新的{{tableComment}}")
    public Result<{{entityName}}VO> create(
            @Parameter(description = "{{tableComment}}信息") @RequestBody @Valid {{entityName}}DTO {{#lowerCase}}{{entityName}}{{/lowerCase}}DTO
    ) {
        {{entityName}}VO result = {{#lowerCase}}{{entityName}}{{/lowerCase}}Service.create({{#lowerCase}}{{entityName}}{{/lowerCase}}DTO);
        return Result.success(result);
    }

    @PutMapping("/{id}")
    @Operation(summary = "更新{{tableComment}}", description = "根据ID更新{{tableComment}}信息")
    public Result<{{entityName}}VO> update(
            @Parameter(description = "{{tableComment}}ID", required = true) @PathVariable @NotNull Long id,
            @Parameter(description = "{{tableComment}}信息") @RequestBody @Valid {{entityName}}DTO {{#lowerCase}}{{entityName}}{{/lowerCase}}DTO
    ) {
        {{entityName}}VO result = {{#lowerCase}}{{entityName}}{{/lowerCase}}Service.update(id, {{#lowerCase}}{{entityName}}{{/lowerCase}}DTO);
        return Result.success(result);
    }

    @DeleteMapping("/{id}")
    @Operation(summary = "删除{{tableComment}}", description = "根据ID删除{{tableComment}}")
    public Result<Void> delete(
            @Parameter(description = "{{tableComment}}ID", required = true) @PathVariable @NotNull Long id
    ) {
        {{#lowerCase}}{{entityName}}{{/lowerCase}}Service.delete(id);
        return Result.success();
    }
}`,

      'pom.xml.hbs': `<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>{{groupId}}</groupId>
    <artifactId>{{artifactId}}</artifactId>
    <version>{{version}}</version>
    <packaging>jar</packaging>

    <name>{{projectName}}</name>
    <description>{{description}}</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>8</java.version>
        <knife4j.version>4.4.0</knife4j.version>
        <mybatis-plus.version>3.5.3</mybatis-plus.version>
        <druid.version>1.2.18</druid.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Knife4j OpenAPI3 Spring Boot Starter -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
            <version>\${knife4j.version}</version>
        </dependency>

        <!-- MySQL Connector -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>

        <!-- MyBatis Plus Boot Starter -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>\${mybatis-plus.version}</version>
        </dependency>

        <!-- Druid Spring Boot Starter -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>\${druid.version}</version>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- Spring Boot Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>`
    }

    return templates[filename] || ''
  }

  /**
   * 生成并下载完整项目
   */
  async generateProject(): Promise<void> {
    await this.loadTemplates()
    
    const zip = new JSZip()
    
    // 生成项目结构
    await this.generateProjectStructure(zip)
    
    // 生成压缩包并下载
    const content = await zip.generateAsync({ type: "blob" })
    saveAs(content, `${this.config.projectName}.zip`)
  }

  /**
   * 生成项目结构
   */
  private async generateProjectStructure(zip: JSZip): Promise<void> {
    const basePackagePath = this.config.basePackage.replace(/\./g, "/")
    const srcPath = `src/main/java/${basePackagePath}`
    const resourcesPath = "src/main/resources"

    // 1. 项目配置文件
    await this.generateProjectFiles(zip)

    // 2. Spring Boot 配置
    await this.generateConfigFiles(zip, resourcesPath)

    // 3. 通用类
    await this.generateCommonFiles(zip, srcPath)

    // 4. 为每个表生成代码
    for (const table of this.tables) {
      await this.generateTableFiles(zip, srcPath, table)
    }

    // 5. 主启动类
    await this.generateMainClass(zip, srcPath)
  }

  /**
   * 生成项目配置文件
   */
  private async generateProjectFiles(zip: JSZip): Promise<void> {
    const projectData = {
      groupId: this.config.basePackage,
      artifactId: this.config.projectName.toLowerCase().replace(/\s+/g, '-'),
      version: this.config.version,
      projectName: this.config.projectName,
      description: this.config.description,
      author: this.config.author
    }

    // POM文件
    const pomTemplate = this.templates.get('pom.xml.hbs')
    if (pomTemplate) {
      const pomContent = this.renderer.render(pomTemplate, projectData)
      zip.file("pom.xml", pomContent)
    }

    // README文件
    const readmeContent = this.generateReadme()
    zip.file("README.md", readmeContent)

    // .gitignore文件
    const gitignoreContent = this.generateGitignore()
    zip.file(".gitignore", gitignoreContent)
  }

  /**
   * 生成配置文件
   */
  private async generateConfigFiles(zip: JSZip, resourcesPath: string): Promise<void> {
    const configData = {
      port: 8080,
      contextPath: "/",
      applicationName: this.config.projectName,
      basePackage: this.config.basePackage,
      databaseConfig: this.config.database,
      author: this.config.author,
      version: this.config.version,
      projectName: this.config.projectName,
      description: this.config.description
    }

    // application.yml
    const appYmlTemplate = this.templates.get('application.yml.hbs')
    if (appYmlTemplate) {
      const appYmlContent = this.renderer.render(appYmlTemplate, configData)
      zip.file(`${resourcesPath}/application.yml`, appYmlContent)
    }

    // Knife4j配置
    const knife4jTemplate = this.templates.get('knife4j-config.hbs')
    if (knife4jTemplate) {
      const knife4jContent = this.renderer.render(knife4jTemplate, configData)
      const configPath = `src/main/java/${this.config.basePackage.replace(/\./g, "/")}/config`
      zip.file(`${configPath}/Knife4jConfiguration.java`, knife4jContent)
    }
  }

  /**
   * 生成通用类文件
   */
  private async generateCommonFiles(zip: JSZip, srcPath: string): Promise<void> {
    const commonData = {
      basePackage: this.config.basePackage,
      author: this.config.author,
      version: this.config.version,
      createTime: new Date().toISOString().split('T')[0]
    }

    const commonFiles = [
      { template: 'result.hbs', path: 'common/Result.java' },
      { template: 'page-result.hbs', path: 'common/PageResult.java' },
      { template: 'error-code.hbs', path: 'common/ErrorCode.java' }
    ]

    for (const file of commonFiles) {
      const template = this.templates.get(file.template)
      if (template) {
        const content = this.renderer.render(template, commonData)
        zip.file(`${srcPath}/${file.path}`, content)
      }
    }
  }

  /**
   * 为表生成所有相关文件
   */
  private async generateTableFiles(zip: JSZip, srcPath: string, table: DataTable): Promise<void> {
    const tableData = this.renderer.generateTableData(table)
    const entityName = this.toPascalCase(table.name)

    const files = [
      { template: 'entity.hbs', path: `entity/${entityName}.java` },
      { template: 'dto.hbs', path: `dto/${entityName}DTO.java` },
      { template: 'vo.hbs', path: `vo/${entityName}VO.java` },
      { template: 'mapper.hbs', path: `mapper/${entityName}Mapper.java` },
      { template: 'service.hbs', path: `service/${entityName}Service.java` },
      { template: 'service-impl.hbs', path: `service/impl/${entityName}ServiceImpl.java` },
      { template: 'controller.hbs', path: `controller/${entityName}Controller.java` }
    ]

    for (const file of files) {
      const template = this.templates.get(file.template)
      if (template) {
        const content = this.renderer.render(template, tableData)
        zip.file(`${srcPath}/${file.path}`, content)
      }
    }

    // Mapper XML文件
    const mapperXmlTemplate = this.templates.get('mapper.xml.hbs')
    if (mapperXmlTemplate) {
      const mapperXmlContent = this.renderer.render(mapperXmlTemplate, tableData)
      zip.file(`src/main/resources/mapper/${entityName}Mapper.xml`, mapperXmlContent)
    }
  }

  /**
   * 生成主启动类
   */
  private async generateMainClass(zip: JSZip, srcPath: string): Promise<void> {
    const className = this.toPascalCase(this.config.projectName) + "Application"
    const mainClassContent = `package ${this.config.basePackage};

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * ${this.config.projectName} 启动类
 *
 * @author ${this.config.author}
 * @since ${new Date().toISOString().split('T')[0]}
 */
@SpringBootApplication
@MapperScan("${this.config.basePackage}.mapper")
public class ${className} {

    public static void main(String[] args) {
        SpringApplication.run(${className}.class, args);
        System.out.println("🚀 ${this.config.projectName} 启动成功！");
        System.out.println("📖 接口文档地址: http://localhost:8080/doc.html");
    }
}`

    zip.file(`${srcPath}/${className}.java`, mainClassContent)
  }

  /**
   * 生成README文件
   */
  private generateReadme(): string {
    return `# ${this.config.projectName}

${this.config.description}

## 技术栈

- Spring Boot 2.7.18
- MyBatis Plus 3.5.3
- Knife4j 4.4.0 (API文档)
- MySQL 8.0
- Druid 连接池

## 快速开始

### 1. 环境要求

- JDK 8+
- Maven 3.6+
- MySQL 8.0+

### 2. 数据库配置

修改 \`application.yml\` 中的数据库配置：

\`\`\`yaml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/your_database
    username: your_username
    password: your_password
\`\`\`

### 3. 运行项目

\`\`\`bash
mvn spring-boot:run
\`\`\`

### 4. 访问接口文档

启动成功后，访问：http://localhost:8080/doc.html

## 项目结构

\`\`\`
src/
├── main/
│   ├── java/
│   │   └── ${this.config.basePackage.replace(/\./g, '/')}/
│   │       ├── controller/     # 控制器
│   │       ├── service/        # 服务层
│   │       ├── mapper/         # 数据访问层
│   │       ├── entity/         # 实体类
│   │       ├── dto/            # 数据传输对象
│   │       ├── vo/             # 视图对象
│   │       ├── common/         # 通用类
│   │       └── config/         # 配置类
│   └── resources/
│       ├── mapper/             # MyBatis映射文件
│       └── application.yml     # 配置文件
└── test/                       # 测试代码
\`\`\`

## 作者

${this.config.author}

## 许可证

MIT License
`
  }

  /**
   * 生成.gitignore文件
   */
  private generateGitignore(): string {
    return `# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs
hs_err_pid*

# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar

# IDE
.idea/
*.iws
*.iml
*.ipr
.vscode/
.settings/
.project
.classpath

# OS
.DS_Store
Thumbs.db

# Application
logs/
temp/
`
  }

  /**
   * 转换为PascalCase
   */
  private toPascalCase(str: string): string {
    return str.replace(/(^|[\s_-])([a-z])/g, (_, __, letter) => letter.toUpperCase())
  }
}
